/*
 * Copyright (C) 2025 ameise <ameise.wang@gmail.com> - All Rights Reserved
 *
 * This file is part of e3ds.
 *
 * e3ds is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * e3ds is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with e3ds. If not, see <https://www.gnu.org/licenses/>.
 */

package deque

import "fmt"

func NewRing[T any](cap int) *Ring[T] {
	if cap <= 0 {
		panic(fmt.Sprintf("ring-queue capacity[%d] is smaller", cap))
	}

	r := &Ring[T]{
		cell: make([]T, cap),
		cap:  cap,
	}
	return r
}

type Ring[T any] struct {
	cell    []T
	cap     int
	size    int
	tailIdx int
	headIdx int
	zero    T
}

func (ts *Ring[T]) ResetCap(cap int) error {
	if cap <= 0 {
		return ErrCapacityZero
	}
	if ts.tailIdx != ts.headIdx {
		return ErrIsNotEmpty
	}
	if cap > ts.cap {
		ts.cell = make([]T, cap)
	}
	ts.cap = cap
	return nil
}

func (ts *Ring[T]) IsEmpty() bool {
	return ts.size == 0
}

func (ts *Ring[T]) Size() int {
	return ts.size
}

func (ts *Ring[T]) PushFront(v T) error {
	if ts.size >= ts.cap {
		return ErrCapacityNotEnough
	}
	ts.pushFront(v)
	return nil
}

func (ts *Ring[T]) PushFrontBatch(vs []T) error {
	if ts.size+len(vs) > ts.cap {
		return ErrCapacityNotEnough
	}
	for _, v := range vs {
		ts.pushFront(v)
	}
	return nil
}

func (ts *Ring[T]) PushBack(v T) error {
	if ts.size >= ts.cap {
		return ErrCapacityNotEnough
	}
	ts.pushBack(v)
	return nil
}

func (ts *Ring[T]) PushBackBatch(vs []T) error {
	if ts.size+len(vs) > ts.cap {
		return ErrCapacityNotEnough
	}
	for _, v := range vs {
		ts.pushBack(v)
	}
	return nil
}

func (ts *Ring[T]) PopFront() (T, error) {
	if ts.size == 0 {
		return ts.zero, ErrEmpty
	}
	return ts.popFront(), nil
}

func (ts *Ring[T]) PopFrontBatch(n int) ([]T, error) {
	if ts.size < n {
		return nil, ErrShort
	}
	out := make([]T, 0, n)
	for i := 0; i < n; i++ {
		out = append(out, ts.popFront())
	}
	return out, nil
}

func (ts *Ring[T]) PopFrontHalf() ([]T, error) {
	n := ts.size / 2
	if n == 0 {
		return nil, ErrShort
	}
	return ts.PopFrontBatch(n)
}

func (ts *Ring[T]) PopFrontAll() ([]T, error) {
	if ts.size == 0 {
		return nil, ErrEmpty
	}
	out := make([]T, 0, ts.size)
	for ts.size > 0 {
		out = append(out, ts.popFront())
	}
	return out, nil
}

func (ts *Ring[T]) PopBack() (T, error) {
	if ts.size == 0 {
		return ts.zero, ErrEmpty
	}
	return ts.popBack(), nil
}

func (ts *Ring[T]) PopBackBatch(n int) ([]T, error) {
	if ts.size < n {
		return nil, ErrShort
	}
	out := make([]T, 0, n)
	for i := 0; i < n; i++ {
		out = append(out, ts.popBack())
	}
	return out, nil
}

func (ts *Ring[T]) PopBackHalf() ([]T, error) {
	n := ts.size / 2
	if n == 0 {
		return nil, ErrShort
	}
	return ts.PopBackBatch(n)
}

func (ts *Ring[T]) PopBackAll() ([]T, error) {
	if ts.size == 0 {
		return nil, ErrEmpty
	}
	out := make([]T, 0, ts.size)
	for ts.size > 0 {
		out = append(out, ts.popBack())
	}
	return out, nil
}

func (ts *Ring[T]) Enqueue(v T) error {
	return ts.PushFront(v)
}

func (ts *Ring[T]) EnqueueBatch(vs []T) error {
	return ts.PushFrontBatch(vs)
}

func (ts *Ring[T]) Dequeue() (T, error) {
	return ts.PopBack()
}

func (ts *Ring[T]) DequeueBatch(n int) ([]T, error) {
	return ts.PopBackBatch(n)
}

func (ts *Ring[T]) DequeueHalf() ([]T, error) {
	return ts.PopBackHalf()
}

func (ts *Ring[T]) DequeueAll() ([]T, error) {
	return ts.PopBackAll()
}

func (ts *Ring[T]) Push(v T) error {
	return ts.PushBack(v)
}

func (ts *Ring[T]) PushBatch(vs []T) error {
	return ts.PushBackBatch(vs)
}

func (ts *Ring[T]) Pop() (T, error) {
	return ts.PopBack()
}

func (ts *Ring[T]) Peek() (T, error) {
	if ts.size == 0 {
		return ts.zero, ErrEmpty
	}
	idx := ts.wrapIndex(ts.tailIdx - 1)
	return ts.cell[idx], nil
}

func (ts *Ring[T]) PopBatch(n int) ([]T, error) {
	return ts.PopBackBatch(n)
}

func (ts *Ring[T]) PopHalf() ([]T, error) {
	return ts.PopBackHalf()
}

func (ts *Ring[T]) PopAll() ([]T, error) {
	return ts.PopBackAll()
}

func (ts *Ring[T]) pushFront(v T) {
	ts.size++
	ts.headIdx = ts.wrapIndex(ts.headIdx - 1)
	ts.cell[ts.headIdx] = v
}

func (ts *Ring[T]) pushBack(v T) {
	ts.cell[ts.tailIdx] = v
	ts.size++
	ts.tailIdx = ts.wrapIndex(ts.tailIdx + 1)
}

func (ts *Ring[T]) popFront() T {
	v := ts.cell[ts.headIdx]
	ts.size--
	ts.headIdx = ts.wrapIndex(ts.headIdx + 1)
	return v
}

func (ts *Ring[T]) popBack() T {
	ts.size--
	ts.tailIdx = ts.wrapIndex(ts.tailIdx - 1)
	v := ts.cell[ts.tailIdx]
	return v
}

// 将索引包装在环形范围内
func (ts *Ring[T]) wrapIndex(index int) int {
	if index < 0 {
		return ts.cap + index
	} else if index >= ts.cap {
		return index - ts.cap
	}
	return index
}
